package geppetto.cat.programs;


import geppetto.cat.alignments.Alignment;
import geppetto.cat.alignments.AlignmentEvaluator;
import geppetto.cat.alignments.AlignmentsSet;
import geppetto.cat.alignments.AlignmentEvaluator.Evaluation;
import geppetto.cat.corpus.BilingualCorpus;
import geppetto.cat.models.AbstractModel;
import geppetto.cat.models.AgreementHMM;
import geppetto.cat.models.HMM;
import geppetto.cat.models.SubstochasticHMM;
import geppetto.cat.models.stats.EStepStats;
import geppetto.phraseHMM.BijectiveHMM;
import geppetto.phraseHMM.RegularHMM;
import geppetto.phraseHMM.SymmetricHMM;

import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;



public class ComparePredictedAligmentSets {
	
	/**
	 * -2 a1 worst than a2 in precision and recall
	 * -1 a1 worst than a2 AER
	 * 0 a1 == a2
	 * 1 a1 better than a2 in AER
	 * 2 a1 better than a2 in precision and recall
	 * @param al1
	 * @param al2
	 * @return
	 */
	public static ArrayList<DiffRes>[] compare(AlignmentsSet al1, AlignmentsSet al2, AlignmentsSet gold){
		ArrayList<DiffRes>[] results = new ArrayList[5];
		for(int i = 0; i <5 ; i++){
			results[i] = new ArrayList<DiffRes>();
		}
		for(int i = 0; i < al1.size(); i++){
			DiffRes result = compareAlignment(al1.get(i) , al2.get(i), gold.get(i));
			results[2+result.result].add(result);
		}
		Collections.sort(results[0], new compareAER());
		Collections.sort(results[1], new compareAER());
		Collections.sort(results[3], new compareAER());
		Collections.sort(results[4], new compareAER());//  
		return results;
	}
	
	
	/**
	 * -2 a1 worst than a2 in precision and recall
	 * -1 a1 worst than a2 AER
	 * 0 a1 == a2
	 * 1 a1 better than a2 in AER
	 * 2 a1 better than a2 in precision and recall
	 * @param al1
	 * @param al2
	 * @return
	 */
	public static DiffRes compareAlignment(Alignment a1, Alignment a2, Alignment gold){
		Evaluation eval1 = AlignmentEvaluator.evaluate(a1,gold);
		Evaluation eval2 = AlignmentEvaluator.evaluate(a2,gold);
		double prec1 = eval1.getPrecision();
		double prec2 = eval2.getPrecision();
		double rec1 = eval1.getRecall();
		double rec2 = eval2.getRecall();
		double aer1 = eval1.getAER();
		double aer2 = eval2.getAER();
		if(prec1 > prec2 && rec1 > rec2){
			return new DiffRes(2,a1.getSentenceNumber(),(prec1-prec2),(rec1-rec2),(aer1-aer2));
		}else if(prec1 < prec2 && rec1 < rec2){
			return new DiffRes(-2,a1.getSentenceNumber(),(prec1-prec2),(rec1-rec2),(aer1-aer2));
		}else if(aer1 < aer2){
			return new DiffRes(-1,a1.getSentenceNumber(),(prec1-prec2),(rec1-rec2),(aer1-aer2));
		}else if(aer1 > aer2){
			return new DiffRes(1,a1.getSentenceNumber(),(prec1-prec2),(rec1-rec2),(aer1-aer2));
		}else{
			return new DiffRes(0,a1.getSentenceNumber(),(prec1-prec2),(rec1-rec2),(aer1-aer2));
		}
	}
	
	public static void printAlignmentComparisson(AlignmentsSet al1, AlignmentsSet al2, AlignmentsSet gold, 
			String methdod1,String method2, PrintStream out){
			ArrayList[] results = compare(al1, al2, gold);
			System.out.println();
			System.out.println(methdod1 + " worst precision and recall than " + method2 + " in " + results[0].size() + " cases ");
			for(int i = 0; i < results[0].size(); i++){
				System.out.print(al1.get(i).getSentenceNumber() + "-(" + al1.get(i)._sourceLen+":"+al1.get(i)._foreignLen+"["
						+EStepStats.prettyPrint(((double)al1.get(i)._sourceLen*1.0/al1.get(i)._foreignLen), "0.00", 2)
						+"]{"
						+ EStepStats.prettyPrint(((DiffRes)results[0].get(i)).precisionDifference, "0.00", 2)+"/"
						+ EStepStats.prettyPrint(((DiffRes)results[0].get(i)).recallDiference, "0.00", 2)+"/"
						+ EStepStats.prettyPrint(((DiffRes)results[0].get(i)).AERDifference, "0.00", 2)
						+"})\t");
			}
			System.out.println();
			System.out.println(methdod1 + " worst AER than " + method2 + " in " + results[1].size() + " cases ");
			for(int i = 0; i < results[1].size(); i++){
				System.out.print(al1.get(i).getSentenceNumber() + "-(" + al1.get(i)._sourceLen+":"+al1.get(i)._foreignLen+"["
						+EStepStats.prettyPrint(((double)al1.get(i)._sourceLen*1.0/al1.get(i)._foreignLen), "0.00", 2)
						+"]{"
						+ EStepStats.prettyPrint(((DiffRes)results[1].get(i)).precisionDifference, "0.00", 2)+"/"
						+ EStepStats.prettyPrint(((DiffRes)results[1].get(i)).recallDiference, "0.00", 2)+"/"
						+ EStepStats.prettyPrint(((DiffRes)results[1].get(i)).AERDifference, "0.00", 2)
						+"})\t");
			}
			System.out.println();
			System.out.println(methdod1 + " Equal " + method2 + " in " + results[2].size() + " cases ");
			for(int i = 0; i < results[2].size(); i++){
				System.out.print(al1.get(i).getSentenceNumber() + "-(" + al1.get(i)._sourceLen+":"+al1.get(i)._foreignLen+"["
						+EStepStats.prettyPrint(((double)al1.get(i)._sourceLen*1.0/al1.get(i)._foreignLen), "0.00", 2)
						+"]{"
						+ EStepStats.prettyPrint(((DiffRes)results[2].get(i)).precisionDifference, "0.00", 2)+"/"
						+ EStepStats.prettyPrint(((DiffRes)results[2].get(i)).recallDiference, "0.00", 2)+"/"
						+ EStepStats.prettyPrint(((DiffRes)results[2].get(i)).AERDifference, "0.00", 2)
						+"})\t");
			}
			System.out.println();
			System.out.println(methdod1 + " Better AER " + method2 + " in " + results[3].size() + " cases ");
			for(int i = 0; i < results[3].size(); i++){
				System.out.print(al1.get(i).getSentenceNumber() + "-(" + al1.get(i)._sourceLen+":"+al1.get(i)._foreignLen+"["
						+EStepStats.prettyPrint(((double)al1.get(i)._sourceLen*1.0/al1.get(i)._foreignLen), "0.00", 2)
						+"]{"
						+ EStepStats.prettyPrint(((DiffRes)results[3].get(i)).precisionDifference, "0.00", 2)+"/"
						+ EStepStats.prettyPrint(((DiffRes)results[3].get(i)).recallDiference, "0.00", 2)+"/"
						+ EStepStats.prettyPrint(((DiffRes)results[3].get(i)).AERDifference, "0.00", 2)
						+"})\t");
			}
			System.out.println();
			System.out.println(methdod1 + " Better Precision and RECALL " + method2 + " in " + results[4].size() + " cases ");
			for(int i = 0; i < results[4].size(); i++){
				System.out.print(al1.get(i).getSentenceNumber() + "-(" + al1.get(i)._sourceLen+":"+al1.get(i)._foreignLen+"["
						+EStepStats.prettyPrint(((double)al1.get(i)._sourceLen*1.0/al1.get(i)._foreignLen), "0.00", 2)
						+"]{"
						+ EStepStats.prettyPrint(((DiffRes)results[4].get(i)).precisionDifference, "0.00", 2)+"/"
						+ EStepStats.prettyPrint(((DiffRes)results[4].get(i)).recallDiference, "0.00", 2)+"/"
						+ EStepStats.prettyPrint(((DiffRes)results[4].get(i)).AERDifference, "0.00", 2)
						+"})\t");
			}
			
			double[] totalPrecionDifference = new double[5];
			double[] totalRecallDifference = new double[5];
			for(int i = 0; i< 5;i++){
				int size = results[i].size();
				double totalPrecion=0, totalRecall=0;
				for(int j = 0; j < results[i].size(); j++){
					totalPrecion += ((DiffRes)results[i].get(j)).precisionDifference;
					totalRecall += ((DiffRes)results[i].get(j)).recallDiference;
				}
				totalPrecionDifference[i]=totalPrecion;
				totalRecallDifference[i]=totalRecall;
			}
			
			System.out.println("\n\nResume\n\n");
			System.out.println(methdod1 + " Worst precision and recall than " + method2 + " in " + results[0].size() + " cases ");
			System.out.println(methdod1 + " Worst AER than " + method2 + " in " + results[1].size() + " cases ");
			System.out.println(methdod1 + " Equal " + method2 + " in " + results[2].size() + " cases ");
			System.out.println(methdod1 + " Better AER " + method2 + " in " + results[3].size() + " cases ");
			System.out.println(methdod1 + " Better Precision and RECALL " + method2 + " in " + results[4].size() + " cases ");
			System.out.println("summ " + results[0].size()+"\t"+results[1].size()+"\t"+results[2].size()+"\t"+results[3].size()+"\t"+results[4].size());
			

			System.out.println(methdod1 + " on average worst p/r  from " + method2 + " of precision"+
					totalPrecionDifference[0]/results[0].size()
					+ " recall "
					+totalRecallDifference[0]/results[0].size()
					);
			System.out.println(methdod1 + " on average worst aer  from " + method2 + " of precision"+
					totalPrecionDifference[1]/results[1].size()
					+ " recall "
					+totalRecallDifference[1]/results[1].size()
					);
			System.out.println(methdod1 + " on average better aer  from " + method2 + " of precision"+
					totalPrecionDifference[3]/results[3].size()
					+ " recall "
					+totalRecallDifference[3]/results[3].size()
					);
			System.out.println(methdod1 + " on average better p/r  from " + method2 + " of precision"+
					totalPrecionDifference[4]/results[4].size()
					+ " recall "
					+totalRecallDifference[4]/results[4].size()
					);
			

			
	}
	
	
	
	
	
	
	//TODO Should go to some global place
	public static class compareAER implements Comparator<DiffRes>{

		public int compare(DiffRes o1, DiffRes o2) {
			if(o1.AERDifference > o2.AERDifference)
				return 1;
			else if (o1.AERDifference > o2.AERDifference){
				return -1;
			}else{
				return 0;
			}
			
		}
		
	}
	public static class DiffRes{
		public int result;
		public int senteceNumber;
		public double precisionDifference, recallDiference,AERDifference;
		public DiffRes(int result, int sentenceNumber, double precisionDifference, double recallDiference,double AERDifference){
			this.result = result;
			this.senteceNumber = sentenceNumber;
			this.precisionDifference = precisionDifference;
			this.recallDiference = recallDiference;
			this.AERDifference = AERDifference;
		}
		
	}
	
	
	
	public static void main(String[] args) throws IOException {
		String corpusDescription = args[0];
		int size = Integer.parseInt(args[1]);
		int maxSentenceSize = Integer.parseInt(args[2]);
		
		System.out.println("Corpus " + corpusDescription);
		System.out.println("Size " + size);
		System.out.println("Max Sent length " + maxSentenceSize);
		
		BilingualCorpus forwardCorpus = BilingualCorpus.getCorpusFromFileDescription(corpusDescription, size,maxSentenceSize);
		BilingualCorpus backwardCorpus = forwardCorpus.reverse();	
	
			
	
		
		String modelDir1 = args[3];
		String modelName1 = args[4];
		Boolean projectTestTime1 = Boolean.parseBoolean(args[5]);
		String decodingType1 = args[6];
		double epsilon1 = Double.parseDouble(args[7]);
		double slack1 = Double.parseDouble(args[8]);
		int maxStep1 = Integer.parseInt(args[9]);
		int maxProjecIter1 = Integer.parseInt(args[10]);
		modelDir1 = modelDir1 + "/" + modelName1 + "/" + forwardCorpus.getName()
		+ "/" + size + "/model/";

		
		System.out.println("Model1 ");
		System.out.println("Model Dir " + modelDir1);
		System.out.println("Model Name " + modelName1);
		System.out.println("Project at test time " + projectTestTime1);
		System.out.println("Decoding type " + decodingType1);
		System.out.println("Epsilon " + epsilon1);
		System.out.println("Slack " + slack1);
		System.out.println("Max Step " + maxStep1);
		System.out.println("Max Project Iterations " + maxProjecIter1);
		
		
		String modelDir2 = args[11];
		String modelName2 = args[12];
		Boolean projectTestTime2 = Boolean.parseBoolean(args[13]);
		String decodingType2 = args[14];
		double epsilon2 = Double.parseDouble(args[15]);
		double slack2 = Double.parseDouble(args[16]);
		int maxStep2 = Integer.parseInt(args[17]);
		int maxProjecIter2 = Integer.parseInt(args[18]);

		modelDir2 = modelDir2 + "/" + modelName2 + "/" + forwardCorpus.getName()
		+ "/" + size + "/model/";

		
		System.out.println("Model2 ");
		System.out.println("Model Dir " + modelDir2);
		System.out.println("Model Name " + modelName2);
		System.out.println("Project at test time " + projectTestTime2);
		System.out.println("Decoding type " + decodingType2);
		System.out.println("Epsilon " + epsilon2);
		System.out.println("Slack " + slack2);
		System.out.println("Max Step " + maxStep2);
		System.out.println("Max Project Iterations " + maxProjecIter2);
		
		//If using f1 decoding
		float f1Balance = Float.parseFloat(args[19]);
		
		
		System.out.println("Balance F1 "  + f1Balance);
		
	
		
			
		AlignmentsSet[] al1 = GenericMethods.getAlignmentSet(forwardCorpus,backwardCorpus, modelDir1, modelName1, 
				projectTestTime1, decodingType1, epsilon1,slack1,maxStep1,maxProjecIter1,f1Balance);
		AlignmentsSet al2[] = GenericMethods.getAlignmentSet(forwardCorpus,backwardCorpus, modelDir2, modelName2, 
				projectTestTime2, decodingType2, epsilon2,slack2,maxStep2,maxProjecIter2,f1Balance);
		printAlignmentComparisson(al1[0], al2[0], forwardCorpus.getGold(), 
				"forward-"+modelName1+"-"+projectTestTime1+"-"+decodingType1,
				"forward-"+modelName2+"-"+projectTestTime2+"-"+decodingType2, 
				System.out);
		printAlignmentComparisson(al1[1], al2[1], backwardCorpus.getGold(), 
				"backward-"+modelName1+"-"+projectTestTime1+"-"+decodingType1,
				"backward-"+modelName2+"-"+projectTestTime2+"-"+decodingType2, 
				System.out);
	}
}
